﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

namespace SensorJanken
{
    using Microsoft.Xna.Framework;
    using MotionGesture;

    public partial class PageJanken : PhoneApplicationPage
    {
        public PageJanken()
        {
            InitializeComponent();

            manualChoki.Tap += new EventHandler<GestureEventArgs>(manualChoki_Tap);
            manualGuu.Tap += new EventHandler<GestureEventArgs>(manualGuu_Tap);
            manualPah.Tap += new EventHandler<GestureEventArgs>(manualPah_Tap);
        }

        void manualChoki_Tap(object sender, GestureEventArgs e)
        {
            if (isManualMode)
            {
                FixedHandManualy(JangKengPongKind.CHOKI);
            }
        }

        void manualGuu_Tap(object sender, GestureEventArgs e)
        {
            if (isManualMode)
            {
                FixedHandManualy(JangKengPongKind.GOO);
            }
        }

        void manualPah_Tap(object sender, GestureEventArgs e)
        {
            if (isManualMode)
            {
                FixedHandManualy(JangKengPongKind.PAH);
            }
        }

        void FixedHandManualy(JangKengPongKind hand)
        {
            if (handResult == JangKengPongKind.NONE)
            {
                handResult = hand;
                SendHandResult(hand);
                switch (hand)
                {
                    case JangKengPongKind.GOO:
                        imageGuu.Opacity = 1;
                        break;
                    case JangKengPongKind.CHOKI:
                        imageChoki.Opacity = 1;
                        break;
                    case JangKengPongKind.PAH:
                        imagePah.Opacity = 1;
                        break;
                }
            }
        }

        MotionGesture gesture;
        MulticastMessaging.MulticastGroup multicastGroup;

        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            gesture = new MotionGesture()
            {
                Threshold = decisionThreshold,
                JudgeDuration=TimeSpan.FromSeconds(3)
            };

            gesture.GestureChanged += new EventHandler<MotionGestureEventArgs>(gesture_GestureChanged);

            imagePah.Opacity = 0;
            imageGuu.Opacity = 0;
            imageChoki.Opacity = 0;

            bool isUseMG = true;
            multicastGroup = new MulticastMessaging.MulticastGroup();
            string groupName;
            if (NavigationContext.QueryString.TryGetValue("Name", out groupName))
            {
                multicastGroup.GroupName = groupName;
                PageTitle.Text = "グループ名 : " + groupName;
            }
            else
            {
                isUseMG = false;
            }
            string groupAddress;
            if (NavigationContext.QueryString.TryGetValue("Address", out groupAddress))
            {
                multicastGroup.GroupAddress = groupAddress;
            }
            else
            {
                isUseMG = false;
            }
            string groupPort;
            if (NavigationContext.QueryString.TryGetValue("Port", out groupPort))
            {
                multicastGroup.GroupPort = int.Parse(groupPort);
            }
            else
            {
                isUseMG = false;
            }

            if (isUseMG)
            {
                try
                {
                    multicastGroup.StatusChanged += new EventHandler<MulticastMessaging.MessagingStatusEventArgs>(multicastGroup_StatusChanged);
                    multicastGroup.MessageReceived += new EventHandler<MulticastMessaging.MessagingRecievedEventArgs>(multicastGroup_MessageReceived);
                    multicastGroup.Join();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Multicast GroupにJoinできませんでした:" + ex.Message);
                    isUseMG = false;
                }
            }
            base.OnNavigatedTo(e);
        }

        void multicastGroup_StatusChanged(object sender, MulticastMessaging.MessagingStatusEventArgs e)
        {
            if (e.Status.CompareTo("Joined") == 0)
            {
                tbStatus.Text = "参加済!";
            }
        }

        private int playerNumber = 0;
        private Dictionary<string, JangKengPongKind> otherHandsResult = new Dictionary<string, JangKengPongKind>();
        void multicastGroup_MessageReceived(object sender, MulticastMessaging.MessagingRecievedEventArgs e)
        {
            if (e.Message.StartsWith("Joined"))
            {
                PageTitle.Text = String.Format("グループ名 : {0}({1})", multicastGroup.GroupName, multicastGroup.JoinedMembers + 1);
            }
            else if(e.Message.StartsWith("Hand"))
            {
                string receivedHand = e.Message.Substring("Hand,".Length);
                var otherHand = JangKengPongKind.NONE;
                if (receivedHand.CompareTo(JangKengPongKind.GOO.ToString()) == 0)
                {
                    otherHand = JangKengPongKind.GOO;
                }
                else if (receivedHand.CompareTo(JangKengPongKind.CHOKI.ToString()) == 0)
                {
                    otherHand = JangKengPongKind.CHOKI;
                }
                else if (receivedHand.CompareTo(JangKengPongKind.PAH.ToString()) == 0)
                {
                    otherHand = JangKengPongKind.PAH;
                }
                otherHandsResult[e.Sender.ToString()] = otherHand;
            }
            else if (e.Message.StartsWith("Player"))
            {
                playerNumber++;
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                {
                    tbStatus.Text = "人数:" + playerNumber.ToString();
                });
            }
        }

        float decisionThreshold = 1.3f;
        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
        {
            gesture.GestureChanged -= gesture_GestureChanged;
            gesture.Dispose();

            base.OnNavigatedFrom(e);
        }

        void gesture_GestureChanged(object sender, MotionGestureEventArgs e)
        {
            if (handResult == JangKengPongKind.NONE && e.Status.Type == GestureType.BYUN)
            {
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    lock (gesture)
                    {
                        gesture.Stop();
                        var kind = JangKengPongKind.NONE;
                        switch (e.Status.Direction)
                        {
                            case GestureDirection.X:
                                imageChoki.Opacity = 1;
                                kind = JangKengPongKind.CHOKI;
                                break;
                            case GestureDirection.Y:
                                imageGuu.Opacity = 1;
                                kind = JangKengPongKind.GOO;
                                break;
                            case GestureDirection.Z:
                                imagePah.Opacity = 1;
                                kind = JangKengPongKind.PAH;
                                break;
                        }
                        if (kind != JangKengPongKind.NONE)
                        {
                            handResult = kind;
                            SendHandResult(kind);
                        }
                    }
                });
            }
        }

        private void SendHandResult(JangKengPongKind kind)
        {
            if (multicastGroup.Joined)
            {
                try
                {
                    multicastGroup.Send("Hand," + kind.ToString());
                }
                catch (Exception ex)
                {
                    MessageBox.Show("結果の送信に失敗しました!:" + ex.Message);
                }
            }
        }

        private bool isPlaying = false;
        private readonly double playingDuration = 1000;
        private int timerCounted;
        private readonly int timerCountValue = 3;
        private System.Windows.Threading.DispatcherTimer timer;
        private JangKengPongKind handResult;

        protected override void OnTap(GestureEventArgs e)
        {
            if (!isCheckingPlayers)
            {
                var position = e.GetPosition(canvasBattleField);
                if (position.Y > 480)
                {
                    return;
                }
                if (!isPlaying)
                {
                    imageChoki.Opacity = 0;
                    imageGuu.Opacity = 0;
                    imagePah.Opacity = 0;

                    timer = new System.Windows.Threading.DispatcherTimer();
                    timer.Interval = TimeSpan.FromMilliseconds(playingDuration);
                    timer.Tick += new EventHandler(timer_Tick);
                    timerCounted = timerCountValue;
                    gesture.Start();
                    timer.Start();

                    isPlaying = true;
                    tbStatus.Text = "Go!";
                    otherHandsResult.Clear();
                    handResult = JangKengPongKind.NONE;

                    PageTitle.Text = String.Format("グループ名 : {0}({1})", multicastGroup.GroupName, multicastGroup.JoinedMembers);
                }
            }
            base.OnTap(e);
        }

        void timer_Tick(object sender, EventArgs e)
        {
            if (timerCounted > 0)
            {
                tbStatus.Text = timerCounted.ToString();
                timerCounted--;
                return;
            }
            lock (gesture)
            {
                if (isPlaying)
                {
                    if (handResult == JangKengPongKind.NONE)
                    {
                        if (multicastGroup.Joined)
                        {
                            tbStatus.Text = "時間切れ!";
                        }
                        gesture.Stop();
                    }
                    else
                    {
                        if (multicastGroup.Joined)
                        {
                            int count = 0;
                            PageTitle.Text = String.Format("グループ名 : {0}({1})", multicastGroup.GroupName, otherHandsResult.Count);
                            JangKengPongResult result = JudgeJungKenResult(out count);
                            if (count > 0)
                            {
                                switch (result)
                                {
                                    case JangKengPongResult.WIN:
                                        tbStatus.Text = "勝ち!";
                                        Microsoft.Devices.VibrateController.Default.Start(TimeSpan.FromSeconds(1));
                                        break;
                                    case JangKengPongResult.LOSE:
                                        tbStatus.Text = "負け!";
                                        break;
                                    case JangKengPongResult.AIKO:
                                        tbStatus.Text = "あいこ!";
                                        break;
                                    default:
                                        tbStatus.Text = "ノーゲーム!";
                                        break;
                                }
                            }
                            else
                            {
                                tbStatus.Text = "一人遊び";
                            }
                        }
                        else
                        {
                            tbStatus.Text = "結果をチェック!";
                        }
                    }
                    isPlaying = false;
                }
                timer.Stop();
            }
        }

        private JangKengPongResult JudgeJungKenResult(out int count)
        {
            var results = new Dictionary<JangKengPongKind, int>();
            foreach (var hand in otherHandsResult)
            {
                if (results.ContainsKey(hand.Value))
                {
                    results[hand.Value]++;
                }
                else
                {
                    results[hand.Value] = 1;
                }
            }

            string otherHandsNo = "";
            List<JangKengPongKind> kinds = new List<JangKengPongKind>() { JangKengPongKind.GOO, JangKengPongKind.CHOKI, JangKengPongKind.PAH };
            count = 0;
            foreach (var kind in kinds)
            {
                if (results.ContainsKey(kind))
                {
                    if (!String.IsNullOrWhiteSpace(otherHandsNo))
                    {
                        otherHandsNo += ",";
                    }
                    count += results[kind];
                    otherHandsNo += string.Format("{0}={1}", kind.ToString(), results[kind]);
                }
            }
            tbOtherHands.Text = otherHandsNo;

            JangKengPongResult result = JangKengPongResult.UNKNOWN;
            if (results.Keys.Count == 1)
            {
                switch (results.Keys.First())
                {
                    case JangKengPongKind.GOO:
                        switch (handResult)
                        {
                            case JangKengPongKind.GOO:
                                result = JangKengPongResult.AIKO;
                                break;
                            case JangKengPongKind.CHOKI:
                                result = JangKengPongResult.LOSE;
                                break;
                            case JangKengPongKind.PAH:
                                result = JangKengPongResult.WIN;
                                break;
                        }
                        break;
                    case JangKengPongKind.CHOKI:
                        switch (handResult)
                        {
                            case JangKengPongKind.GOO:
                                result = JangKengPongResult.WIN;
                                break;
                            case JangKengPongKind.CHOKI:
                                result = JangKengPongResult.AIKO;
                                break;
                            case JangKengPongKind.PAH:
                                result = JangKengPongResult.LOSE;
                                break;
                        }
                        break;
                    case JangKengPongKind.PAH:
                        switch (handResult)
                        {
                            case JangKengPongKind.GOO:
                                result = JangKengPongResult.LOSE;
                                break;
                            case JangKengPongKind.CHOKI:
                                result = JangKengPongResult.WIN;
                                break;
                            case JangKengPongKind.PAH:
                                result = JangKengPongResult.AIKO;
                                break;
                        }
                        break;
                }
            }
            else if (results.Keys.Count == 2)
            {
                if (results.ContainsKey(handResult))
                {
                    var otherHands = results.Keys;
                    var myHand = handResult;
                    var otherHand = JangKengPongKind.NONE;
                    if (otherHands.ElementAt(0) == handResult)
                    {
                        otherHand = otherHands.ElementAt(1);
                    }
                    else
                    {
                        otherHand = otherHands.ElementAt(0);
                    }
                    result = JangKengPongResult.AIKO;
                    switch (myHand)
                    {
                        case JangKengPongKind.GOO:
                            switch (otherHand)
                            {
                                case JangKengPongKind.CHOKI:
                                    result = JangKengPongResult.WIN;
                                    break;
                                case JangKengPongKind.PAH:
                                    result = JangKengPongResult.LOSE;
                                    break;
                            }
                            break;
                        case JangKengPongKind.CHOKI:
                            switch (otherHand)
                            {
                                case JangKengPongKind.GOO:
                                    result = JangKengPongResult.LOSE;
                                    break;
                                case JangKengPongKind.PAH:
                                    result = JangKengPongResult.WIN;
                                    break;
                            }
                            break;
                        case JangKengPongKind.PAH:
                            switch (otherHand)
                            {
                                case JangKengPongKind.GOO:
                                    result = JangKengPongResult.WIN;
                                    break;
                                case JangKengPongKind.CHOKI:
                                    result = JangKengPongResult.LOSE;
                                    break;
                            }
                            break;
                    }
                }
                else
                {
                    result = JangKengPongResult.AIKO;
                }
            }
            else if (results.Keys.Count == 3)
            {
                result = JangKengPongResult.AIKO;
            }

            return result;
        }

        enum JangKengPongKind
        {
            NONE,
            GOO,
            CHOKI,
            PAH
        }

        enum JangKengPongResult
        {
            UNKNOWN,
            WIN,
            LOSE,
            AIKO
        }

        private bool isManualMode = false;
        private void cbManual_Checked(object sender, RoutedEventArgs e)
        {
            isManualMode = true;
            manualChoki.Opacity = 1;
            manualGuu.Opacity = 1;
            manualPah.Opacity = 1;
        }

        private void cbManual_Unchecked(object sender, RoutedEventArgs e)
        {
            isManualMode = false;
            manualPah.Opacity = 0.3;
            manualGuu.Opacity = 0.3;
            manualChoki.Opacity = 0.3;
        }

        private bool isCheckingPlayers = false;
        private string[] buttonCheckPlayersContent = { "参加者チェック", "チェック完了" };
        private void buttonCheckPlayers_Click(object sender, RoutedEventArgs e)
        {
            if (!isPlaying)
            {
                if (buttonCheckPlayersContent[0].CompareTo(buttonCheckPlayers.Content) == 0)
                {
                    if (multicastGroup.Joined)
                    {
                        try
                        {
                            multicastGroup.Send("Player");
                            MessageBox.Show(String.Format("参加先：{0},{1},{2}", multicastGroup.GroupAddress, multicastGroup.GroupPort, multicastGroup.GroupName));
                            buttonCheckPlayers.Content = buttonCheckPlayersContent[1];
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Player情報の送信に失敗しました!:" + ex.Message);
                        }
                        isCheckingPlayers = true;
                    }
                    else
                    {
                        MessageBox.Show("Wi-Fi LANのグループにJoinしていません");
                    }
                }
                else
                {
                    playerNumber = 0;
                    buttonCheckPlayers.Content = buttonCheckPlayersContent[0];
                    tbStatus.Text = "Ready";
                    isCheckingPlayers = false;
                }
            }
        }
    }
}